﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.
using System;
using System.Linq;
using System.Threading.Tasks;

namespace McopyAPI
{
    class McopyAPI
    {
        //default parametr
        static bool bUseMulticore = false;
        static bool bAllwaysOverrideFiles = false;
        static int iCountCopiedFiles = 0;
        static int iCountOverridedFiles = 0;
        static int iCountCreatedDirectory = 0;
        static bool bCopyOneFile = false;

        static void Main(string[] args)
        {
            //validate parametrs count
            if (args.Length != 2)
            {
                if (args.Length == 1)
                {
                    string arg = args[0];

                    //display help
                    if (String.Compare(arg, "/?", false) == 0)
                    {
                        Console.WriteLine("McopyAPI is program to copy long path in windows shell");
                        Console.WriteLine("Write at Ms-Pl license, read here http://www.opensource.org/licenses/ms-pl.html");
                        Console.WriteLine("McopyAPI version 1.0.0.6");
                        Console.WriteLine("Homepage project http://mcopyapi.codeplex.com");
                        Console.WriteLine("");
                        
                        Console.WriteLine("MCOPYAPI source [destination] [/M] [/O] [/F]");
                        Console.WriteLine("");
                        Console.WriteLine("source        Specifies files and subdirectores to be copied");
                        Console.WriteLine(@"              example: c:\source or \\SERVER\source or \\127.0.0.1\source");
                        Console.WriteLine("");
                        Console.WriteLine("destination   Specifies destination new files and subdirectores");
                        Console.WriteLine(@"              example: c:\destination or \\SERVER\destination");
                        Console.WriteLine(@"                       or \\127.0.0.1\destination");
                        Console.WriteLine("");
                        Console.WriteLine("/M            Use all core procesor in you computer");
                        Console.WriteLine("");
                        Console.WriteLine("/O            Always overwrite files even if they are identical");
                        Console.WriteLine("              default not overwrite files if last write time source file");
                        Console.WriteLine("              is equal or less last write time destination file");
                        Console.WriteLine("");
                        Console.WriteLine("/F            Copy specified file (source and destination must be a file)");
                        Console.WriteLine("              default specified source and destination is a directory");
                        
                        return;
                    }
                    else
                    {
                        Console.WriteLine("Invalid parametrs -> break program!");
                        return;
                    }
                }
                else if (args.Length > 2)
                {
                    Array.ForEach(args, ar =>
                    {
                        if (String.Compare(ar.ToUpper(), "/M", false) == 0)
                            bUseMulticore = true;
                        if (String.Compare(ar.ToUpper(), "/O", false) == 0)
                            bAllwaysOverrideFiles = true;
                        if (String.Compare(ar.ToUpper(), "/F", false) == 0)
                            bCopyOneFile = true;
                    });                                               
                }
                else
                {
                    Console.WriteLine("Invalid parametrs -> break program!");
                    return;
                }                
            }

            //reader parametrs
            string sourceDir = args[0];
            string destinationDir = args[1];
           
            //limit chars 32000
            if (sourceDir.Length > 32000 ||
                destinationDir.Length > 32000)
            {
                Console.WriteLine("Path length is too long -> break program!");
                return;
            }

            //path exists?
            if (bCopyOneFile)
            {
                if (!APIFunction.ExistsFile(sourceDir))
                {
                    Console.WriteLine("Source path doesn't exist or is unavailable -> break program!");
                    return;
                }
            }
            else
            {
                if (!APIFunction.ExistsDirectory(sourceDir))
                {
                    Console.WriteLine("Source path doesn't exist or is unavailable -> break program!");
                    return;
                }
                if (!APIFunction.ExistsDirectory(destinationDir))
                {
                    Console.WriteLine("Destination path doesn't exist or is unavailable -> break program!");
                    return;
                }
            }

            var startTime = DateTime.Now;
            Console.WriteLine("Start Program: " + startTime);

            try
            {
                if (bCopyOneFile)
                {
                    CopyOneFile(sourceDir, destinationDir);
                }
                else
                {
                    var t = Task.Factory.StartNew(() => CopyLongFolder(sourceDir, destinationDir));
                    t.Wait();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("McopyAPI error: " + ex.Message);
            }

            var stopTime = DateTime.Now;
            Console.WriteLine("Stop Program: " + stopTime);
            Console.WriteLine("Count copied files      : " + iCountCopiedFiles);
            Console.WriteLine("Count overrided files   : " + iCountOverridedFiles);
            Console.WriteLine("Count created directory : " + iCountCreatedDirectory);
            Console.WriteLine("All time copy this      : " + (stopTime - startTime));
                
            //bellow line only test develop
            //Console.ReadLine();                        
        }

        private static void CopyOneFile(string sourceFile, string destinationFile)
        {
            if (bAllwaysOverrideFiles)
            {
                if (String.Compare(sourceFile, destinationFile, false) == 0)
                {
                    iCountOverridedFiles++;
                    APIFunction.CopyFile(sourceFile, destinationFile, true);
                    Console.WriteLine(String.Format("Override file: {0} -> {1}",                                                                        APIFunction.ConvertToLongPath(sourceFile),
                                                    APIFunction.ConvertToLongPath(destinationFile)));
                }
                else
                {
                    iCountCopiedFiles++;
                    APIFunction.CopyFile(sourceFile, destinationFile, true);
                    Console.WriteLine(String.Format("Copy new file: {0} -> {1}",
                                                    APIFunction.ConvertToLongPath(sourceFile),
                                                    APIFunction.ConvertToLongPath(destinationFile)));
                }
            }
            else
            {
                iCountCopiedFiles++;
                APIFunction.CopyFile(sourceFile, destinationFile, false);
                Console.WriteLine(String.Format("Copy new file: {0} -> {1}",
                                                APIFunction.ConvertToLongPath(sourceFile),
                                                APIFunction.ConvertToLongPath(destinationFile)));
            }
        }

        private static void CopyLongFolder(string sourceDir, string destinationDir)
        {
            if (!APIFunction.ExistsDirectory(destinationDir))
            {
                APIFunction.Create(destinationDir);
                iCountCreatedDirectory++;
                Console.WriteLine("Create directory: " + APIFunction.ConvertToLongPath(destinationDir));
            }

            if (bUseMulticore)
            {
                var t1 = Task.Factory.StartNew(() => Parallel.ForEach(APIFunction.EnumerateFiles(sourceDir), (sFile, loopStatus) =>
                {
                    CopyAllFilesInDirectory(destinationDir, sFile);
                }));
                t1.Wait();
            }
            else
            {
                foreach (string sFile in APIFunction.EnumerateFiles(sourceDir))
                {
                    CopyAllFilesInDirectory(destinationDir, sFile);
                }
            }

            string[] tabDir = APIFunction.EnumerateDirectories(sourceDir).ToArray();

            if (bUseMulticore)
            {
                Parallel.ForEach(tabDir, (sDir, loopStatus) =>
                {
                    CopyAllSubDirectoresInDirectory(sourceDir, destinationDir, sDir);
                });
                return;
            }
            Array.ForEach(tabDir, sDir => CopyAllSubDirectoresInDirectory(sourceDir, destinationDir, sDir));
        }

        private static void CopyAllSubDirectoresInDirectory(string sourceDir, string destinationDir, string sDir)
        {
            string dDir = sDir;
            dDir = dDir.Replace(sourceDir, destinationDir);
            CopyLongFolder(sDir, dDir);
        }

        private static void CopyAllFilesInDirectory(string destinationDir, string sFile)
        {
            string dFile = String.Format("{0}\\{1}", 
                           destinationDir, 
                           sFile.Substring(sFile.LastIndexOf("\\") + 1,
                           sFile.Length - sFile.LastIndexOf("\\") - 1));

            if (!bAllwaysOverrideFiles)
            {
                if (APIFunction.ExistsFile(dFile))
                {
                    if (APIFunction.IsSourceFileNewerThanDestinationFile(sFile, dFile))
                    {
                        APIFunction.CopyFile(APIFunction.ConvertToLongPath(sFile), APIFunction.ConvertToLongPath(dFile), true);
                        iCountOverridedFiles++;
                        Console.WriteLine(String.Format("Override file: {0} -> {1}",                                                                        APIFunction.ConvertToLongPath(sFile), 
                                                        APIFunction.ConvertToLongPath(dFile)));
                    }
                    else
                    {
                        Console.WriteLine("File not changed => " + APIFunction.ConvertToLongPath(dFile));
                    }
                }
                else
                {
                    APIFunction.CopyFile(APIFunction.ConvertToLongPath(sFile), APIFunction.ConvertToLongPath(dFile), true);
                    iCountCopiedFiles++;
                    Console.WriteLine(String.Format("Copy new file: {0} -> {1}", 
                                                    APIFunction.ConvertToLongPath(sFile),
                                                    APIFunction.ConvertToLongPath(dFile)));
                }
            }
            else
            {
                APIFunction.CopyFile(APIFunction.ConvertToLongPath(sFile), APIFunction.ConvertToLongPath(dFile), true);
                iCountOverridedFiles++;
                Console.WriteLine(String.Format("Override file: {0} -> {1}", 
                                                APIFunction.ConvertToLongPath(sFile), 
                                                APIFunction.ConvertToLongPath(dFile)));
            }
        }
    }
}
